#include "TimeWheel.h"
#include <func.h>

/**
 * 初始化时间轮
 */
void timewheel_init(TimeWheel* tw, int slots, int interval) {
    // 分配轮盘空间，每个槽初始化为空
    tw->wheel = (Node**)calloc(slots, sizeof(Node*));
    tw->slots = slots;
    tw->interval = interval;
    tw->current = 0; //当前时间轮指针位置
    vector_init(&tw->vec); //初始化映射数组，用来存放fd，管理关闭任务
    tw->last = time(NULL); //记录上一次跳动时间
}

/**
 * 将 connfd 插入到时间轮
 */
void timewheel_add(TimeWheel* tw, int connfd) {
    Node* node;
    // 当前节点大于结点容量 或 当前元素不在数组中
    if ((size_t)connfd >= tw->vec.capacity || tw->vec.elements[connfd] == NULL) {
        // 文件描述符不存在
        // 创建新节点,并连接
        node = (Node*)malloc(sizeof(Node));
        node->connfd = connfd;

        vector_add(&tw->vec, connfd, node);
    } else {//不在数组中，则先移除旧的，下面再执行插入
        node = timewheel_remove(tw, connfd);  
    }
    
    // 用头插法,插入到当前下标位置的头部
    node->index = tw->current;
    node->prev = NULL;
    node->next = tw->wheel[tw->current];
    if (tw->wheel[tw->current]) {
        tw->wheel[tw->current]->prev = node;
    }
    tw->wheel[tw->current] = node;

    printf("DEBUG: add %d at %d\n", connfd, tw->current);
}

/**
 * 移除 connfd
 */
Node* timewheel_remove(TimeWheel* tw, int connfd) {
    // 因为是双向链表，所以前后都要改变
    //记录结点位置，用来移除
    Node* node = tw->vec.elements[connfd];

    //如果不是头结点，先让前结点的next指向下一个，进行跳过
    if (node->prev) {
        node->prev->next = node->next;
    } else {
        //不存在前节点，直接该下标为，放下一个结点
        tw->wheel[node->index] = node->next;
    }

    //如果结点的下个存在，则更新下个结点的前结点
    if (node->next) {
        node->next->prev = node->prev;
    }
    return node;
}

/**
 * 时间轮跳动 (tick)
 * 轮盘指针移动，触发超时事件
 */
void timewheel_tick(TimeWheel* tw) {

    // current 只决定新插入的结点的轮盘位置 ,所以先每次前进更新
    tw->current = (tw->current + 1) % tw->slots; // 轮盘前进

    // 若更新后，发现当前位置已经有fd，则当前fd是上一轮插入的元素，则进行移除
    // 处理当前槽的所有超时连接
    Node* curr = tw->wheel[tw->current];
    while (curr) {
        close(curr->connfd); // 关闭连接
        printf("connfd=%d timeout, closing connection\n", curr->connfd);
        // 释放节点
        tw->vec.elements[curr->connfd] = NULL;
        Node* next = curr->next;
        free(curr);
        curr = next;
    }
    tw->wheel[tw->current] = NULL; // 清空当前槽
    tw->last = time(NULL);
}

/**
 * 释放时间轮
 */
void timewheel_destroy(TimeWheel* tw) {
    //遍历所有槽位，并释放
    for (int i = 0; i < tw->slots; i++) {
        Node* curr = tw->wheel[i];
        //一个轮盘位置，有多个节点,用curr相连接
        while (curr) {
            //记录下个next位置,再释放当前位置
            Node* next = curr->next;
            free(curr);
            curr = next;
        }
    }
    free(tw->wheel);
    vector_destroy(&tw->vec);
}
